/** @file

Copyright (c) 2006-2023, Kunlun BIOS, Kunlun Technology (Beijing) Co., Ltd.. All
Rights Reserved.

You may not reproduce, distribute, publish, display, perform, modify, adapt,
transmit, broadcast, present, recite, release, license or otherwise exploit
any part of this publication in any form, by any means, without the prior
written permission of Kunlun Technology (Beijing) Co., Ltd..

Module Name:


Abstract:


Revision History:

**/

#include <Boot.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/PrintLib.h>
//add-klk-lyang-P000A-start//
#include <Library/KlSetupVarRWLib.h>
#include <Library/UefiBootManagerLib.h>
#include <Library/KlDevicePathOrderLib.h>
//add-klk-lyang-P000A-end//
#include <Protocol/LoadedImage.h>
#include <Library/FileExplorerLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/LoadFile.h>
#include <Protocol/FirmwareVolume2.h>
#include <Protocol/SimpleFileSystem.h>
#include <Protocol/LoadFile.h>
#include <Protocol/GraphicsOutput.h>
#include <Pi/PiFirmwareFile.h>
#include <Pi/PiFirmwareVolume.h>
#include <Library/SetupHiiDataManagerLib.h>

STATIC EFI_CALLBACK_INFO                            *mBootCallBackInfo = NULL;
VOID                                                *mStartOpCodeHandle = NULL;
VOID                                                *mEndOpCodeHandle = NULL;
EFI_IFR_GUID_LABEL                                  *mStartLabel = NULL;
EFI_IFR_GUID_LABEL                                  *mEndLabel = NULL;

EFI_GUID mBootMaintGuid                             = FORMSET_ID_GUID_BOOT;

BOOT_DEV_INFO                                       *mBootDevInfo     = NULL;
UINTN                                               mBootDevInfoCount = 0;
// STATIC SYSTEM_SETUP_CONFIGURATION                   mSysSetupConfig;

BOOT_TEMP_VAR                     mBootTempVar;
EFI_GUID                          mBootTempGuid = BOOT_TEMP_ORDER_GUID;
// EFI_GUID  mSetupVarGuid    = SYSTEM_CONFIGURATION_GUID;
// CHAR16    mSetupName[] = L"SystemSetupConfig";
extern UINT16  BootDeviceOrder[8];
UINT16    *mBootOptionNum = NULL;
extern EFI_HII_DATABASE_PROTOCOL             *gLibHiiDatabase;
extern EFI_HII_CONFIG_ROUTING_PROTOCOL       *gLibHiiConfigRouting;
///
/// Boot Option from variable Menu
///
BM_MENU_OPTION      BootOptionMenu = {
  BM_MENU_OPTION_SIGNATURE,
  {NULL},
  0
};

EFI_STATUS
EFIAPI
BootRouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
);

EFI_STATUS
EFIAPI
BootExtractConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Request,
  OUT EFI_STRING                             *Progress,
  OUT EFI_STRING                             *Results
  );


CHAR16 *
DevicePathToStr (
  IN EFI_DEVICE_PATH_PROTOCOL     *DevPath
  );
EFI_STATUS
Var_UpdateBootOrder (
  VOID
  );

// VOID
// GetBootOrder (
//   IN  EFI_CALLBACK_INFO    *CallbackData
//   );

EFI_STATUS
UpdateBootOrderLabels (
  IN     EFI_HII_HANDLE                         BootHiiHandle,
  IN OUT SYSTEM_SETUP_CONFIGURATION             *SetupConfig
  );

/**
  Compare two device paths to check if they are exactly same.

  @param DevicePath1    A pointer to the first device path data structure.
  @param DevicePath2    A pointer to the second device path data structure.

  @retval TRUE    They are same.
  @retval FALSE   They are not same.

**/
BOOLEAN
CompareDevicePath (
  IN EFI_DEVICE_PATH_PROTOCOL *DevicePath1,
  IN EFI_DEVICE_PATH_PROTOCOL *DevicePath2
  )
{
  UINTN Size1;
  UINTN Size2;

  Size1 = GetDevicePathSize (DevicePath1);
  Size2 = GetDevicePathSize (DevicePath2);

  if (Size1 != Size2) {
    return FALSE;
  }

  if (CompareMem (DevicePath1, DevicePath2, Size1) != 0) {
    return FALSE;
  }

  return TRUE;
}

/**
  Set value of a data element in an Array by its Index.

  @param  Array                  The data array.
  @param  Type                   Type of the data in this array.
  @param  Index                  Zero based index for data in this array.
  @param  Value                  The value to be set.

**/
VOID
SetArrayData (
  IN VOID                     *Array,
  IN UINT8                    Type,
  IN UINTN                    Index,
  IN UINT64                   Value
  )
{

  ASSERT (Array != NULL);

  switch (Type) {
  case EFI_IFR_TYPE_NUM_SIZE_8:
    *(((UINT8 *) Array) + Index) = (UINT8) Value;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_16:
    *(((UINT16 *) Array) + Index) = (UINT16) Value;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_32:
    *(((UINT32 *) Array) + Index) = (UINT32) Value;
    break;

  case EFI_IFR_TYPE_NUM_SIZE_64:
    *(((UINT64 *) Array) + Index) = (UINT64) Value;
    break;

  default:
    break;
  }
}
/**
  Return data element in an Array by its Index.

  @param  Array                  The data array.
  @param  Type                   Type of the data in this array.
  @param  Index                  Zero based index for data in this array.

  @retval Value                  The data to be returned

**/
UINT64
GetArrayData (
  IN VOID                     *Array,
  IN UINT8                    Type,
  IN UINTN                    Index
  )
{
  UINT64 Data;

  ASSERT (Array != NULL);

  Data = 0;
  switch (Type) {
  case EFI_IFR_TYPE_NUM_SIZE_8:
    Data = (UINT64) *(((UINT8 *) Array) + Index);
    break;

  case EFI_IFR_TYPE_NUM_SIZE_16:
    Data = (UINT64) *(((UINT16 *) Array) + Index);
    break;

  case EFI_IFR_TYPE_NUM_SIZE_32:
    Data = (UINT64) *(((UINT32 *) Array) + Index);
    break;

  case EFI_IFR_TYPE_NUM_SIZE_64:
    Data = (UINT64) *(((UINT64 *) Array) + Index);
    break;

  default:
    break;
  }

  return Data;
}

/**
  Brief description of InitBootMenu.

  @param  HiiHandle

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
InitBootMenu (
  IN EFI_HII_HANDLE                         HiiHandle
  )
{
  //EFI_STATUS                                Status;
  UINT16                                    *BootOrder;
  UINT16                                    BootDeviceNum;
  UINTN                                     BootOrderSize;

  BootOrder     = NULL;
  BootOrderSize = 0;
  //
  // Get the BootOrder from the Var
  //
  GetEfiGlobalVariable2 (L"BootOrder", (VOID **) &BootOrder, &BootOrderSize);
  if (BootOrder == NULL) {
    DEBUG ((EFI_D_ERROR, "BootOrder == NULL!!!!!!: "));
    return EFI_NOT_FOUND;
  }
  if (BootOrderSize != 0) {
    BootDeviceNum = (UINT16) (BootOrderSize / sizeof(UINT16));
  } else {
    BootDeviceNum = 0;
    BootOrder     = NULL;
  }
  (gSetupDataManagment->SUCInfo)->BootOrder          = AllocateCopyPool (BootOrderSize, BootOrder);
  (gSetupDataManagment->SUCInfo)->AdvBootDeviceNum = BootDeviceNum;
  UpdateBootOrderLabels (HiiHandle,NULL);
  return EFI_SUCCESS;
}

/**
  Brief description of BootConfigSyncToBootDevInfo.

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
BootConfigSyncToBootDevInfo (
  VOID
  )
{
  UINT16                                              BootDevCount;
  UINTN                                               Index;
  BOOLEAN                                             BoolStaus;

  BootDevCount = gSetupDataManagment->SUCInfo->AdvBootDeviceNum;
  BoolStaus = HiiGetBrowserData (&mBootTempGuid, L"BootTempVar", sizeof (BOOT_TEMP_VAR), (UINT8 *) &mBootTempVar);
  if (!BoolStaus) {
    DEBUG ((DEBUG_ERROR, "%a %d BootOptionOrder %d\n", __FUNCTION__, __LINE__,mBootTempVar.BootOptionOrder[0]));
    return EFI_SUCCESS;
  }
  DEBUG ((DEBUG_ERROR, "%a %d  BoolStaus %d\n", __FUNCTION__, __LINE__,BoolStaus));
  ZeroMem(mBootOptionNum,BootDevCount*sizeof(UINT16));

  for (Index = 0; Index < BootDevCount; Index++) {
    if (mBootTempVar.BootOptionOrder[Index] == 0) {
      return EFI_SUCCESS;
    }
    mBootOptionNum[Index] = (mBootTempVar.BootOptionOrder[Index]-1);
    // mBootDevInfo[Index].BootOptionNum = (mSysSetupConfig.BootOptionOrder[Index]-1);
  }

  return EFI_SUCCESS;
}


EFI_STATUS
EFIAPI
LocalLoadApp(
  IN CONST EFI_GUID                        *NameGuid
  )
{

  EFI_STATUS                               Status;
  UINTN                                    HandleCount;
  EFI_HANDLE                               *HandleBuffer;
  MEDIA_FW_VOL_FILEPATH_DEVICE_PATH        FvFilePath;
  UINTN                                    Index;
  EFI_FIRMWARE_VOLUME2_PROTOCOL            *Fv;
  UINTN                                    Size;
  EFI_FV_FILETYPE                          Type;
  EFI_FV_FILE_ATTRIBUTES                   Attributes;
  UINT32                                   AuthenticationStatus;
  EFI_DEVICE_PATH_PROTOCOL                 *DevicePath;
  EFI_DEVICE_PATH_PROTOCOL                 *AppDevicePath;
  EFI_HANDLE                               ImageHandle;
  UINTN                                    ExitDataSize;
  CHAR16                                   *ExitData;


  HandleCount  = 0;
  HandleBuffer = NULL;

  Status = gBS->LocateHandleBuffer (
                  ByProtocol,
                  &gEfiFirmwareVolume2ProtocolGuid,
                  NULL,
                  &HandleCount,
                  &HandleBuffer
                  );
  if (EFI_ERROR(Status)) {
    return Status;
  }

  EfiInitializeFwVolDevicepathNode (&FvFilePath, NameGuid);

  for (Index = 0; Index < HandleCount; Index++) {
    Status = gBS->HandleProtocol (
                    HandleBuffer[Index],
                    &gEfiFirmwareVolume2ProtocolGuid,
                    (VOID **) &Fv
                    );
    if (!EFI_ERROR (Status)) {
      Status = Fv->ReadFile (
                     Fv,
                     NameGuid,
                     NULL,
                     &Size,
                     &Type,
                     &Attributes,
                     &AuthenticationStatus
                     );
    }
    if (EFI_ERROR (Status)) {
      continue;
    }

    //
    // Create device path of Update application
    //
    DevicePath = DevicePathFromHandle (HandleBuffer[Index]);
    if (DevicePath == NULL) {
      continue;
    }

    AppDevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &FvFilePath);
    if (AppDevicePath == NULL) {
      continue;
    }

    Status = gBS->LoadImage (
                    TRUE,
                    gImageHandle,
                    AppDevicePath,
                    NULL,
                    0,
                    &ImageHandle
                    );
    FreePool (AppDevicePath);
    if (EFI_ERROR (Status)) {
      continue;
    }

    gBS->StartImage (ImageHandle, &ExitDataSize, &ExitData);

    break;
  }
  FreePool (HandleBuffer);

  return Status;
}

/**
  Brief description of BootCallbackRoutine.

  @param  This
  @param  Action
  @param  QuestionId
  @param  Type
  @param  Value
  @param  ActionRequest

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
EFIAPI
BootCallbackRoutine (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  EFI_BROWSER_ACTION                     Action,
  IN  EFI_QUESTION_ID                        QuestionId,
  IN  UINT8                                  Type,
  IN  EFI_IFR_TYPE_VALUE                     *Value,
  OUT EFI_BROWSER_ACTION_REQUEST             *ActionRequest
  )
{
  UINT8                              Index;
  UINT16                             BootDevCount;
  UINT16                             ArrayData[MAX_BOOT_MENU_NUMBER];
  UINT8                              Num;
  UINT8                              Count;
  UINT16                             ArrayValue;

  if ((QuestionId == BOOT_OPTION_ORDER_QUESTION_ID) && (Action == EFI_BROWSER_ACTION_DEFAULT_STANDARD)) {
    BootDevCount = (gSetupDataManagment->SUCInfo)->AdvBootDeviceNum;
    Count = 0;
    for (Num = 0;Num < sizeof(BootDeviceOrder)/sizeof(UINT16);Num++) {
      for (Index = 0; Index < BootDevCount; Index++) {
        if (mBootDevInfo[Index].IsActive == FALSE) {
          continue;
        }
        if (mBootDevInfo[Index].DevType == BootDeviceOrder[Num]) {
          ArrayData[Count] = mBootDevInfo[Index].BootOptionNum + 1;
          Count++;
        }
      }
    }
    for (Index = 0; Index < Count; Index ++) {
      ArrayValue = (UINT16)GetArrayData(Value, EFI_IFR_TYPE_NUM_SIZE_16, Index);
      mBootOptionNum[Index] = ArrayData[Index] - 1;
      if (ArrayValue != ArrayData[Index]) {
        DEBUG ((DEBUG_ERROR, "%a %d  \n", __FUNCTION__, __LINE__));
        SetArrayData (Value, EFI_IFR_TYPE_NUM_SIZE_16, Index, ArrayData[Index]);
      }
    }
    CopyMem(&mBootTempVar.BootOptionOrder, ArrayData,Count*2);
    return EFI_SUCCESS;
  }

  if (Action == EFI_BROWSER_ACTION_CHANGING) {
    Action = EFI_BROWSER_ACTION_CHANGED;
  }

  if (Action != EFI_BROWSER_ACTION_CHANGED) {
    return EFI_UNSUPPORTED;
  }
  *ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
  BootConfigSyncToBootDevInfo();

  return EFI_SUCCESS;
}

/**
  Brief description of BootRouteConfig.

  @param  This
  @param  Configuration
  @param  Progress

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
EFIAPI
BootRouteConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Configuration,
  OUT EFI_STRING                             *Progress
)
{
  EFI_STATUS                        Status;
  BOOT_TEMP_VAR                     BootTempVar;
  EFI_GUID                          BootTempGuid = BOOT_TEMP_ORDER_GUID;
  UINTN                             BufferSize;
  // SYSTEM_SETUP_CONFIGURATION        *SetupVar = NULL;

  Status = GenericRouteConfig(This,Configuration,Progress);
  if (EFI_ERROR (Status)) {
      *Progress = Configuration;
      if(!HiiIsConfigHdrMatch (Configuration, &BootTempGuid, L"BootTempVar")){
        return EFI_NOT_FOUND;
      }
      BufferSize = sizeof (BOOT_TEMP_VAR);
      Status = gRT->GetVariable (
                    L"BootTempVar",
                    &BootTempGuid,
                    NULL,
                    &BufferSize,
                    &(BootTempVar)
                    );
      if (EFI_ERROR (Status)) {
        return EFI_NOT_FOUND;
      }
      //
      // Check if configuring Name/Value storage
      //
      if (StrStr (Configuration, L"OFFSET") == NULL) {
        return EFI_SUCCESS;
      }
      // Convert <ConfigResp> to buffer data by helper function ConfigToBlock()
      //
      Status = gLibHiiConfigRouting->ConfigToBlock (
                                      gLibHiiConfigRouting,
                                      Configuration,
                                      (UINT8 *) &BootTempVar,
                                      &BufferSize,
                                      Progress
                                      );
      if (EFI_ERROR (Status)) {
        return Status;
      }
      BufferSize = sizeof (BOOT_TEMP_VAR);
      //
      // Store Buffer Storage back to EFI variable
      //
      Status = gRT->SetVariable(
                      L"BootTempVar",
                      &BootTempGuid,
                      EFI_VARIABLE_BOOTSERVICE_ACCESS,
                      BufferSize,
                      &BootTempVar
                      );
    BootConfigSyncToBootDevInfo();
    Status = Var_UpdateBootOrder ();
  }
  return Status;
}

/**
  This function converts an input device structure to a Unicode string.

  @param DevPath      A pointer to the device path structure.

  @return             A new allocated Unicode string that represents the device path.

**/
CHAR16 *
DevicePathToStr (
  IN EFI_DEVICE_PATH_PROTOCOL     *DevPath
  )
{
 // EFI_STATUS                       Status;
  CHAR16                           *ToText;
  EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *DevPathToText;

  if (DevPath == NULL) {
    return NULL;
  }

  gBS->LocateProtocol (
          &gEfiDevicePathToTextProtocolGuid,
          NULL,
          (VOID **) &DevPathToText
          );
 // ASSERT_EFI_ERROR (Status);
  ToText = DevPathToText->ConvertDevicePathToText (
                            DevPath,
                            FALSE,
                            TRUE
                            );
 // ASSERT (ToText != NULL);
  return ToText;
}

/**
  Brief description of BOpt_GetMenuEntry.

  @param  MenuOption
  @param  MenuNumber

**/
BM_MENU_ENTRY *
BOpt_GetMenuEntry (
  BM_MENU_OPTION      *MenuOption,
  UINTN               MenuNumber
  )
{
  BM_MENU_ENTRY   *NewMenuEntry;
  UINTN           Index;
  LIST_ENTRY      *List;

 // ASSERT (MenuNumber < MenuOption->MenuNumber);

  List = MenuOption->Head.ForwardLink;
  for (Index = 0; Index < MenuNumber; Index++) {
    List = List->ForwardLink;
  }

  NewMenuEntry = CR (List, BM_MENU_ENTRY, Link, BM_MENU_ENTRY_SIGNATURE);

  return NewMenuEntry;
}

/**
  Get option number according to Boot#### and BootOrder variable.
  The value is saved as #### + 1.

  @param CallbackData    The BMM context data.
**/
// VOID
// GetBootOrder (
//   IN  EFI_CALLBACK_INFO    *CallbackData
//   )
// {
//   SYSTEM_SETUP_CONFIGURATION              *BmmConfig;
//   UINT16                    Index;
//   UINT16                    OptionOrderIndex;
//   BM_MENU_ENTRY             *NewMenuEntry;
//  BM_LOAD_CONTEXT           *NewLoadContext;

//   BmmConfig  = &mSysSetupConfig;
//   ZeroMem (BmmConfig->BootOptionOrder, sizeof (BmmConfig->BootOptionOrder));

//   for (Index = 0, OptionOrderIndex = 0; ((Index < BootOptionMenu.MenuNumber) &&
//        (OptionOrderIndex < (sizeof (BmmConfig->BootOptionOrder) / sizeof (BmmConfig->BootOptionOrder[0]))));
//        Index++) {
//     NewMenuEntry   = BOpt_GetMenuEntry (&BootOptionMenu, Index);
//    NewLoadContext = (BM_LOAD_CONTEXT *) NewMenuEntry->VariableContext;
//   BmmConfig->BootOptionOrder[OptionOrderIndex++] = (UINT16) (NewMenuEntry->OptionNumber + 1);
//   }

//   return;
// }


/**
  This function update the "BootOrder" EFI Variable based on
  BMM Formset's NV map. It then refresh BootOptionMenu
  with the new "BootOrder" list.

  @param CallbackData    The BMM context data.

  @retval EFI_SUCCESS             The function complete successfully.
  @retval EFI_OUT_OF_RESOURCES    Not enough memory to complete the function.
  @return The EFI variable can not be saved. See gRT->SetVariable for detail return information.

**/
EFI_STATUS
Var_UpdateBootOrder (
  VOID
  )
{
  EFI_STATUS  Status;
  UINT16      Index;
  UINT16      OrderIndex;
  UINT16      *BootOrder;
  UINTN       BootOrderSize;
  UINT16      OptionNumber;
  UINT16      BootDeviceNum;

  //
  // First check whether BootOrder is present in current configuration
  //
  GetEfiGlobalVariable2 (L"BootOrder", (VOID **) &BootOrder, &BootOrderSize);
  if (BootOrder == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  BootDeviceNum = (gSetupDataManagment->SUCInfo)->AdvBootDeviceNum;
  //
  // OptionOrder is subset of BootOrder
  //
  for (OrderIndex = 0; (OrderIndex < BootDeviceNum) && (mBootTempVar.BootOptionOrder[OrderIndex] != 0); OrderIndex++) {
    for (Index = OrderIndex; Index < BootOrderSize / sizeof (UINT16); Index++) {
      if ((BootOrder[Index] == (UINT16) (mBootOptionNum[OrderIndex])) && (OrderIndex != Index)) {
        OptionNumber = BootOrder[Index];
        CopyMem (&BootOrder[OrderIndex + 1], &BootOrder[OrderIndex], (Index - OrderIndex) * sizeof (UINT16));
        BootOrder[OrderIndex] = OptionNumber;
      }
    }
  }
  Status = gRT->SetVariable (
                  L"BootOrder",
                  &gEfiGlobalVariableGuid,
                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,
                  BootOrderSize,
                  BootOrder
                  );
  //DEBUG((EFI_D_ERROR, "lenovo: In Boot.c, line %d. Status %r\n", __LINE__,Status));
  FreePool (BootOrder);


  return Status;
}

/**
 Initialize boot device info.

 @retval EFI_SUCCESS              Initialize boot device info successfully.
 @retval EFI_OUT_OF_RESOURCES     Allocate memory failed.
 @retval Other                    Fail to get browser data.
**/
EFI_STATUS
BootDevInfoInit (
  VOID
  )
{
  UINT16                                    *BootOrder;
  UINT16                                    BootDeviceNum;
  UINT16                                    Index;
  UINT16                                    BootOptionName[20];
  UINT8                                     *BootOption;
  UINT8                                     *Ptr;
  UINTN                                     BootOptionSize;
  UINT32                                    Attribute;
  CHAR16                                    *Description;
  UINTN                                     DescriptionSize;
  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;
  UINT16                                    DevicePathSize;

  //DEBUG ((EFI_D_INFO, "Device Infor Init .\n "));
  BootOrder     = (gSetupDataManagment->SUCInfo)->BootOrder;
  BootDeviceNum = (gSetupDataManagment->SUCInfo)->AdvBootDeviceNum;
  mBootDevInfoCount = 0;
  if (mBootDevInfo != NULL) {
    FreePool (mBootDevInfo);
    mBootDevInfo = NULL;
  }
  if (BootDeviceNum == 0) {
    return EFI_SUCCESS;
  }
  mBootOptionNum = AllocateZeroPool (BootDeviceNum * sizeof(UINT16));
  mBootDevInfo = AllocateZeroPool (BootDeviceNum * sizeof(BOOT_DEV_INFO));
  if (mBootDevInfo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  mBootDevInfoCount = BootDeviceNum;

  for (Index = 0; Index < BootDeviceNum; Index++) {
    mBootDevInfo[Index].BootOptionNum = BootOrder[Index];
    UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootOrder[Index]);
    GetEfiGlobalVariable2 (BootOptionName, (VOID **) &BootOption, &BootOptionSize);

    if (BootOption == NULL) {
      continue;
    }

    Ptr = BootOption;
    Attribute = *((UINT32 *) Ptr);
    Ptr += sizeof (UINT32);
    DevicePathSize = *((UINT16 *) Ptr);
    Ptr += sizeof (UINT16);
    Description = (CHAR16 *) Ptr;
    DescriptionSize = StrSize (Description);
    Ptr += DescriptionSize;
    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) Ptr;
    Ptr += DevicePathSize;

    mBootDevInfo[Index].IsActive     = (BOOLEAN) ((Attribute & LOAD_OPTION_ACTIVE) == LOAD_OPTION_ACTIVE);
    mBootDevInfo[Index].IsEfiBootDev = (BOOLEAN) ((DevicePath->Type != BBS_DEVICE_PATH) || (DevicePath->SubType != BBS_BBS_DP));
    (mBootDevInfo[Index].BootDeviceString).BootOrderIndex = BootOrder[Index];
    (mBootDevInfo[Index].BootDeviceString).pString          = AllocateCopyPool (DescriptionSize, Description);
    (mBootDevInfo[Index].BootDeviceString).DevicePath       = AllocateCopyPool (DevicePathSize, DevicePath);
    mBootDevInfo[Index].DevType = GetBootDeviceTypeFromDevicePath((mBootDevInfo[Index].BootDeviceString).DevicePath);
    FreePool (BootOption);
  }

  return EFI_SUCCESS;
}

/**
  Get all boot devices information and order from BootOrder.

  @param[in]  BootOrder            Pointer to BootOrder
  @param[in]  BootDeviceNum        Number of boot devices in BootOrder
  @param[out] BootOrderString      Double pointer to all boot devices information

  @retval EFI_SUCCESS              Get all boot devices information successfully
  @retval EFI_OUT_OF_RESOURCES     Allocate pool fail or number of boot device exceed maximum boot device count
**/
EFI_STATUS
GetBootOrderString (
  IN  UINT16                                *BootOrder,
  IN  UINT16                                BootDeviceNum,
  OUT STRING_PTR                            **BootOrderString
  )
{
  UINT16                                    Index;
  UINT16                                    BootOptionName[20];
  UINT8                                     *BootOption;  
  UINTN                                     BootOptionSize;
  UINT8                                     *TempPtr;
  EFI_DEVICE_PATH_PROTOCOL                  *DevicePath;
  CHAR16                                    *DeviceName;
  UINTN                                     DeviceNameSize;
  UINT16                                    DevPathLen;

  if (BootDeviceNum == 0) {
    return EFI_SUCCESS;
  }

  *BootOrderString = AllocateZeroPool (sizeof (STRING_PTR) * BootDeviceNum);
  if (*BootOrderString == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }

  Index = 0;
  while (Index < BootDeviceNum) {
    if (Index >= MAX_BOOT_MENU_NUMBER) {
      return EFI_OUT_OF_RESOURCES;
    }
    UnicodeSPrint (
      BootOptionName,
      sizeof (BootOptionName),
      L"Boot%04x",
      BootOrder[Index]
      );
  GetEfiGlobalVariable2 (BootOptionName, (VOID **) &BootOption, &BootOptionSize);
    if (BootOption == NULL) {
      gBS->FreePool (BootOrder);
      return EFI_OUT_OF_RESOURCES;
    }

    TempPtr = BootOption;
    TempPtr += sizeof (UINT32);
    DevPathLen = *(UINT16 *) TempPtr;
    TempPtr += sizeof (UINT16);
    DeviceName = (CHAR16 *) TempPtr;
    DeviceNameSize = StrSize (DeviceName);
    TempPtr += DeviceNameSize;
    DevicePath = (EFI_DEVICE_PATH_PROTOCOL *) TempPtr;

    (*BootOrderString)[Index].BootOrderIndex   = BootOrder[Index];
    (*BootOrderString)[Index].pString          = AllocateCopyPool (DeviceNameSize, DeviceName);
    if ((*BootOrderString)[Index].pString == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    (*BootOrderString)[Index].DevicePath       = AllocateCopyPool (DevPathLen, DevicePath);
    if ((*BootOrderString)[Index].DevicePath == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    gBS->FreePool (BootOption);
    Index++;

  }
  return EFI_SUCCESS;
}

/**
  Brief description of UpdateBootOrder.

  @param  HiiHandle
  @param  BootDeviceList
  @param  FormId
  @param  Offset

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
UpdateBootOrder (
  IN EFI_HII_HANDLE                            HiiHandle,
  IN BOOT_DEV_INFO                             *BootDeviceList,
  IN EFI_FORM_ID                               FormId,
  IN UINT16                                    Offset
  )
{
  //EFI_STATUS                                   Status;
  EFI_IFR_GUID_LABEL                           *StartLabel;
  EFI_IFR_GUID_LABEL                           *EndLabel;
  VOID                                         *StrartOpCodeHandle;
  VOID                                         *EndOpCodeHandle;
  UINTN                                        Index;
  STRING_REF                                   TempLastToken;
  UINTN                                        BootDevCount;
  VOID                                         *OptionsOpCodeHandle;
  UINT8                                        *OpcodePtr;
//add-klk-lyang-P000A-start//
  UINT8                                        ActiveDevCount;
//add-klk-lyang-P000A-end//
  BootDevCount = gSetupDataManagment->SUCInfo->AdvBootDeviceNum;
  ActiveDevCount = 0;

  StrartOpCodeHandle       = HiiAllocateOpCodeHandle ();
  StartLabel               = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (StrartOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
  StartLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  StartLabel->Number       = FORM_BOOT_LABLE;
  //
  // Create Hii Extend Label OpCode as the end opcode
  //
  EndOpCodeHandle = HiiAllocateOpCodeHandle ();
  EndLabel = (EFI_IFR_GUID_LABEL *) HiiCreateGuidOpCode (EndOpCodeHandle, &gEfiIfrTianoGuid, NULL, sizeof (EFI_IFR_GUID_LABEL));
  EndLabel->ExtendOpCode = EFI_IFR_EXTEND_OP_LABEL;
  EndLabel->Number       = LABEL_END;
  TempLastToken = 0;
  //DEBUG ((EFI_D_ERROR, "boot device count: %d.\n ", BootDevCount));
  OptionsOpCodeHandle = HiiAllocateOpCodeHandle ();
  for (Index = 0; Index < BootDevCount; Index++, TempLastToken = 0) {
    if (BootDeviceList[Index].IsActive == FALSE) {
      continue;
    }

     //DEBUG ((EFI_D_ERROR, "HII Set Boot device string: %s.\n ", (BootDeviceList[Index].BootDeviceString).pString));
     TempLastToken = HiiSetString (
                        HiiHandle,
                        0,
                        (BootDeviceList[Index].BootDeviceString).pString,
                        NULL
                        );
    OpcodePtr = HiiCreateOneOfOptionOpCode (
                  OptionsOpCodeHandle,
                  TempLastToken,
                  0,
                  EFI_IFR_TYPE_NUM_SIZE_16,
                  (BootDeviceList[Index].BootOptionNum+1)
                  );
      ASSERT(OpcodePtr != NULL);
      OpcodePtr = NULL;
      ActiveDevCount++;
      //DEBUG ((EFI_D_INFO, "Create orderlist option: %d.\n ", Index));
  }
//DEBUG((EFI_D_ERROR, "lenovo: In Boot.c, line %d. ActiveDevCount %d\n", __LINE__,ActiveDevCount));
//modify-klk-lyang-P000A-start//
  if (ActiveDevCount > 0) {
//modify-klk-lyang-P000A-end//
      OpcodePtr = HiiCreateOrderedListOpCode (
                    StrartOpCodeHandle,                          // Container for dynamic created opcodes
                    BOOT_OPTION_ORDER_QUESTION_ID,               // Question ID
                    BOOT_TEMP_VARSTORE_ID,                   // VarStore I
                    Offset,                                      // Offset in Buffer Storage
                    STRING_TOKEN (STR_CHANGE_ORDER),             // Question prompt text
                    STRING_TOKEN (STR_CHANGE_ORDER_HELP),             // Question help text
                    EFI_IFR_FLAG_CALLBACK,                       // Question flag
                    0,                                           // Ordered list flag, e.g. EFI_IFR_UNIQUE_SET
                    EFI_IFR_TYPE_NUM_SIZE_16,                    // Data type of Question value
                    ActiveDevCount + 2,                          // Maximum container
                    OptionsOpCodeHandle,                         // Option Opcode list
                    NULL                                         // Default Opcode is NULL
                    );
      ASSERT(OpcodePtr != NULL);
      OpcodePtr = NULL;
    //DEBUG ((EFI_D_INFO, "Create orderlist OK.\n "));
  }  

  //
  //We need remove the same data we update to HII database before.
  //
  HiiUpdateForm (
    HiiHandle,
    &mBootMaintGuid,
    FormId,
    StrartOpCodeHandle,
    EndOpCodeHandle
    );

  HiiFreeOpCodeHandle (OptionsOpCodeHandle);
  HiiFreeOpCodeHandle (StrartOpCodeHandle);
  HiiFreeOpCodeHandle (EndOpCodeHandle);
  return EFI_SUCCESS;
}

/**
  Update boot order related menu option in action opcode in platform to boot page.

  @param  BootHiiHandle          Hii hanlde for Boot page
  @param  SetupConfig            Pointer to SYSTEM_SETUP_CONFIGURATION instance

  @retval EFI_SUCCESS            Update boot menu successful
  @retval Other                  Some error occured in update boot menu

**/
EFI_STATUS
UpdateBootOrderLabels (
  IN     EFI_HII_HANDLE                         BootHiiHandle,
  IN OUT SYSTEM_SETUP_CONFIGURATION             *SetupConfig
  )
{
  // UINT16                                VarOffset;
  //
  // if there is no BootOption, skip to build from.
  //
  if ((gSetupDataManagment->SUCInfo)->AdvBootDeviceNum == 0) {
    return EFI_SUCCESS;
  }
  
  BootDevInfoInit ();
  // VarOffset = BOOT_OPTION_ORDER_VAR_OFFSET;

  UpdateBootOrder (
    BootHiiHandle,
    mBootDevInfo,
    FORM_BOOT_CHG_ID,
    0
    );

  return EFI_SUCCESS;
}

/**
  Brief description of BootExtractConfig.

  @param  This
  @param  Request
  @param  Progress
  @param  Results

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
EFIAPI
BootExtractConfig (
  IN  CONST EFI_HII_CONFIG_ACCESS_PROTOCOL   *This,
  IN  CONST EFI_STRING                       Request,
  OUT EFI_STRING                             *Progress,
  OUT EFI_STRING                             *Results
  )
{
  EFI_STATUS                    Status;
  UINTN                         BufferSize;
  EFI_CALLBACK_INFO             *CallbackInfo;
  EFI_HANDLE                    DriverHandle;
  EFI_GUID                      VarStoreGuid = SYSTEM_CONFIGURATION_GUID;
  UINTN                         Size;
  CHAR16                        *StrPointer;
  EFI_STRING                    ConfigRequestHdr;
  EFI_STRING                    ConfigRequest;
  BOOLEAN                       AllocatedRequest;
  VOID*                         VarPtr;
  SYSTEM_ACCESS                 SystemAccess;
  BOOT_TEMP_VAR                 BootTempVar;
  SYSTEM_SETUP_CONFIGURATION    SysSetupConfig;
  EFI_GUID                      SysAccessGuid = SYSTEM_ACCESS_GUID;
  EFI_GUID                      BootTempGuid = BOOT_TEMP_ORDER_GUID;
  EFI_GUID                      SysConfigGuid = SYSTEM_CONFIGURATION_GUID;
  Status = EFI_SUCCESS;
  if (Progress == NULL || Results == NULL) {
    return EFI_INVALID_PARAMETER;
  }
  BufferSize = sizeof (SYSTEM_SETUP_CONFIGURATION);
  Status = gRT->GetVariable (
                  SETUP_VARIABLE_NAME,
                  &SysConfigGuid,
                  NULL,
                  &BufferSize,
                  &(SysSetupConfig)
                  );
  if (EFI_ERROR (Status)) {
    return EFI_NOT_FOUND;
  }

  VarPtr = &SysSetupConfig;
  *Progress = Request;
  CallbackInfo = EFI_CALLBACK_INFO_FROM_THIS (This);
  BufferSize       = GetVarStoreSize (CallbackInfo->HiiHandle, &CallbackInfo->FormsetGuid, &VarStoreGuid, "SystemSetupConfig");
  ConfigRequestHdr = NULL;
  ConfigRequest    = NULL;
  AllocatedRequest = FALSE;
  ConfigRequest = Request;
  if (Request == NULL) {
    //
    // Request is set to NULL, construct full request string.
    //
    //
    // Allocate and fill a buffer large enough to hold the <ConfigHdr> template
    // followed by "&OFFSET=0&WIDTH=WWWWWWWWWWWWWWWW" followed by a Null-terminator
    //
    Status = gLibHiiDatabase->GetPackageListHandle (gLibHiiDatabase, CallbackInfo->HiiHandle, &DriverHandle);
    if (EFI_ERROR (Status)) {
      return EFI_NOT_FOUND;
    }
    ConfigRequestHdr = HiiConstructConfigHdr (&SysConfigGuid, L"SystemSetupConfig", DriverHandle);
    if (ConfigRequestHdr == NULL) return EFI_NOT_FOUND;
    Size = (StrLen (ConfigRequestHdr) + 32 + 1) * sizeof (CHAR16);
  ConfigRequest = AllocateZeroPool (Size);
    if (ConfigRequest == NULL) {
      return EFI_OUT_OF_RESOURCES;
    }
    AllocatedRequest = TRUE;
    UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", ConfigRequestHdr, (UINT64) BufferSize);
    FreePool (ConfigRequestHdr);
    ConfigRequestHdr = NULL;
  } else {
    //
    // Check routing data in <ConfigHdr>.
    // Note: if only one Storage is used, then this checking could be skipped.
    //
    if (!HiiIsConfigHdrMatch (Request, &SysConfigGuid, L"SystemSetupConfig")) {
      if(!HiiIsConfigHdrMatch (Request, &SysAccessGuid, L"SystemAccess")){
        if(!HiiIsConfigHdrMatch (Request, &BootTempGuid, L"BootTempVar")){
          return EFI_NOT_FOUND;
        }else{
          BufferSize = sizeof (BOOT_TEMP_VAR);
          Status = gRT->GetVariable (
                          L"BootTempVar",
                          &BootTempGuid,
                          NULL,
                          &BufferSize,
                          &(BootTempVar)
                          );
          if (EFI_ERROR (Status)) {
            return EFI_NOT_FOUND;
          }
          VarPtr = &BootTempVar;
        }
      }else{
        BufferSize = sizeof (SYSTEM_ACCESS);
        Status = gRT->GetVariable (
                        L"SystemAccess",
                        &SysAccessGuid,
                        NULL,
                        &BufferSize,
                        &(SystemAccess)
                        );
        if (EFI_ERROR (Status)) {
          return EFI_NOT_FOUND;
        }
        VarPtr = &SystemAccess;
      }
    }
    if (StrStr (Request, L"OFFSET") == NULL) {
      StrPointer = StrStr (Request, L"PATH");
      if (StrPointer == NULL) {
        return EFI_INVALID_PARAMETER;
      }
      if (StrStr (StrPointer, L"&") == NULL) {
        Size = (StrLen (Request) + 32 + 1) * sizeof (CHAR16);
        ConfigRequest = AllocateZeroPool (Size);
        if (ConfigRequest == NULL) {
          return EFI_OUT_OF_RESOURCES;
        }
        AllocatedRequest = TRUE;
        UnicodeSPrint (ConfigRequest, Size, L"%s&OFFSET=0&WIDTH=%016LX", Request, (UINT64) BufferSize);
      }
    }
  }
  if (StrStr (ConfigRequest, L"OFFSET") == NULL) {
    return EFI_NOT_FOUND;
  }
  Status = gLibHiiConfigRouting->BlockToConfig (
                                   gLibHiiConfigRouting,
                                   ConfigRequest,
                                   (UINT8 *)VarPtr,
                                   BufferSize,
                                   Results,
                                   Progress
                                   );
  if (AllocatedRequest) {
    gBS->FreePool (ConfigRequest);
    ConfigRequest = NULL;
  }
  if (Request == NULL) {
    *Progress = NULL;
  } else if (StrStr (Request, L"OFFSET") == NULL) {
    *Progress = Request + StrLen (Request);
  }
  return Status;
}

/**
  Brief description of InstallBootCallbackRoutine.

  @param  DriverHandle
  @param  HiiHandle

  @retval EFI_SUCCESS   Function successful returned.
**/
EFI_STATUS
InstallBootCallbackRoutine (
  IN EFI_HANDLE                             DriverHandle,
  IN EFI_HII_HANDLE                         HiiHandle
  )
{
  EFI_STATUS                                Status;
  EFI_GUID                                  FormsetGuid = FORMSET_ID_GUID_BOOT;
  UINTN                                     BufferSize;
  mBootCallBackInfo = AllocatePool (sizeof (EFI_CALLBACK_INFO));
  if (mBootCallBackInfo == NULL) {
    return EFI_OUT_OF_RESOURCES;
  }
  mBootCallBackInfo->Signature                    = EFI_CALLBACK_INFO_SIGNATURE;
  mBootCallBackInfo->DriverCallback.ExtractConfig = BootExtractConfig;
  mBootCallBackInfo->DriverCallback.RouteConfig   = BootRouteConfig;
  mBootCallBackInfo->DriverCallback.Callback      = BootCallbackRoutine;
  mBootCallBackInfo->HiiHandle                    = HiiHandle;
  CopyGuid (&mBootCallBackInfo->FormsetGuid, &FormsetGuid);
  BufferSize = sizeof (BOOT_TEMP_VAR);
  Status = gRT->GetVariable (L"BootTempVar", &mBootTempGuid, NULL, &BufferSize, &mBootTempVar);
  if (EFI_ERROR (Status)) {
    ZeroMem(&mBootTempVar, BufferSize);
    Status = gRT->SetVariable(
            L"BootTempVar",
            &mBootTempGuid,
            EFI_VARIABLE_BOOTSERVICE_ACCESS,
            sizeof (BOOT_TEMP_VAR),
            &mBootTempVar
            );

  }
  Status = gBS->InstallProtocolInterface (
                  &DriverHandle,
                  &gEfiHiiConfigAccessProtocolGuid,
                  EFI_NATIVE_INTERFACE,
                  &mBootCallBackInfo->DriverCallback
                  );
  CheckSetupHiiDataManager();
  Status = InitBootMenu(HiiHandle);
  return Status;
}
